/*
 * Copyright© 2000 - 2021 SuperMap Software Co.Ltd. All rights reserved.
 * This program are made available under the terms of the Apache License, Version 2.0
 * which accompanies this distribution and is available at http://www.apache.org/licenses/LICENSE-2.0.html.
*/
package com.supermap.gaf.data.mgt.service.impl;

import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.supermap.gaf.common.storage.client.StorageClient;
import com.supermap.gaf.commontypes.Page;
import com.supermap.gaf.commontypes.tree.DefaultTreeNode;
import com.supermap.gaf.data.mgt.commontype.SysResourceDatasource;
import com.supermap.gaf.data.mgt.mapper.SysResourceDatasourceMapper;
import com.supermap.gaf.data.mgt.model.DatasourceConnectionInfo;
import com.supermap.gaf.data.mgt.service.SysResourceDatasourceService;
import com.supermap.gaf.data.mgt.vo.SysResourceDatasourceSelectVo;
import com.supermap.gaf.exception.GafException;
import com.supermap.gaf.shiro.SecurityUtilsExt;
import com.supermap.gaf.sys.mgt.client.SysDictClient;
import com.supermap.gaf.utils.TreeUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.validation.constraints.NotEmpty;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 数据源服实现务类
 * @author wangxiaolong
 * @date:2021/3/25
 *
 */
@Service
public class SysResourceDatasourceServiceImpl implements SysResourceDatasourceService {
    /**
     * 密钥长度
     */
    public static final int LENGTH_16 = 16;
    public static final int LENGTH_32 = 32;

    @Autowired
    private SysResourceDatasourceMapper sysResourceDatasourceMapper;
    @Autowired
    private SysDictClient sysDictClient;

    @Autowired
    @Qualifier("DatamgtStorageClient")
    private StorageClient storageClient;

    @Value("${gaf.database.secretKey:}")
    private String secretKey;

    public static void main(String[] args) {
        System.out.println(String.format("%s+:",null));
    }
    private String encrypt(String text, String secretKey) {
        if (StringUtils.isEmpty(secretKey)) {
            throw new GafException("未配置数据库密码秘钥");
        }
        if(StringUtils.isEmpty(text)) {
            return text;
        }
        byte[] key = secretKey.getBytes(StandardCharsets.UTF_8);
        if (key.length != LENGTH_16 && key.length != LENGTH_32) {
            throw new GafException("数据库密码秘钥长度非法，应为16位或32位非中文字符");
        }
        SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.AES, key);
        return aes.encryptHex(text);
    }

    @Override
    public String decrypt(String CipherPassword) {
        return decrypt(CipherPassword,secretKey);
    }

    private String decrypt(String text, String secretKey) {
        if (StringUtils.isEmpty(secretKey)) {
            throw new GafException("未配置数据库密码秘钥");
        }
        if(StringUtils.isEmpty(text)) {
            return text;
        }
        byte[] key = secretKey.getBytes(StandardCharsets.UTF_8);
        if (key.length != LENGTH_16 && key.length != LENGTH_32) {
            throw new GafException("数据库密码秘钥长度非法，应为16位或32位非中文字符");
        }
        SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.AES, key);
        return aes.decryptStr(text);
    }

    @Override
    public SysResourceDatasource getById(String datasourceId) {
        if (datasourceId == null) {
            throw new IllegalArgumentException("datasourceId不能为空");
        }
        SysResourceDatasource sysResourceDatasource = sysResourceDatasourceMapper.select(datasourceId);
        if (!StringUtils.isEmpty(sysResourceDatasource.getPassword())) {
            sysResourceDatasource.setPassword(decrypt(sysResourceDatasource.getPassword(), secretKey));
        }
        return sysResourceDatasource;
    }

    @Override
    public List<SysResourceDatasource> listByIds(@NotEmpty Collection<String> datasourceIds) {
        if (datasourceIds == null || datasourceIds.isEmpty()) {
            return Collections.emptyList();
        }
        List<SysResourceDatasource> sysResourceDatasources = sysResourceDatasourceMapper.selectByIds(datasourceIds);
        sysResourceDatasources.forEach(sysResourceDatasource -> {
            if (!StringUtils.isEmpty(sysResourceDatasource.getPassword())) {
                sysResourceDatasource.setPassword(decrypt(sysResourceDatasource.getPassword(), secretKey));
            }
        });
        return sysResourceDatasources;
    }


    @Override
    public Page<SysResourceDatasource> listByPageCondition(SysResourceDatasourceSelectVo sysResourceDatasourceSelectVo, int pageNum, int pageSize) {
        PageInfo<SysResourceDatasource> pageInfo = PageHelper.startPage(pageNum, pageSize).doSelectPageInfo(() -> sysResourceDatasourceMapper.selectList(sysResourceDatasourceSelectVo));
        Page<SysResourceDatasource> page = new Page<>();
        page.setPageIndex(pageInfo.getPageNum());
        page.setPageSize(pageInfo.getPageSize());
        page.setTotal((int)pageInfo.getTotal());
        List<SysResourceDatasource> pagelist = pageInfo.getList();
        if (pagelist != null && pagelist.size() > 0) {
            pagelist.forEach(sysResourceDatasource -> {
                if (!StringUtils.isEmpty(sysResourceDatasource.getPassword())) {
                    sysResourceDatasource.setPassword(decrypt(sysResourceDatasource.getPassword(), secretKey));
                }
            });
        }
        page.setContent(pagelist);
        page.calculateTotalPage();
        return page;
    }


	@Override
    public SysResourceDatasource insertSysResourceDatasource(SysResourceDatasource sysResourceDatasource) {
        Assert.notNull(sysResourceDatasource.getAddr(),"addr不能为null");
        Assert.notNull(sysResourceDatasource.getTypeCode(),"typeCode不能为null");
        List<SysResourceDatasource>  exists = sysResourceDatasourceMapper.selectList(SysResourceDatasourceSelectVo.builder().addr(sysResourceDatasource.getAddr()).typeCodes(Arrays.asList(sysResourceDatasource.getTypeCode())).build());
        boolean isFileType = ("UDB".equals(sysResourceDatasource.getTypeCode()) ||  "UDBX".equals(sysResourceDatasource.getTypeCode()));
        if(!CollectionUtils.isEmpty(exists)){
            if(isFileType){
                throw new GafException("该数据源已注册");
            }
            String format = "%s:%s";
            String cur = String.format(format,sysResourceDatasource.getPort(),sysResourceDatasource.getDbName());
            for(SysResourceDatasource item:exists){
                if(cur.equals(String.format(format,item.getPort(),item.getDbName()))){
                    throw new GafException("该数据源已注册");
                }
            }
        }


        sysResourceDatasource.setDatasourceId(UUID.randomUUID().toString());
        String notEncryptPassword = sysResourceDatasource.getPassword();
        if (!StringUtils.isEmpty(sysResourceDatasource.getPassword())) {
            sysResourceDatasource.setPassword(encrypt(sysResourceDatasource.getPassword(), secretKey));
        }
        sysResourceDatasourceMapper.insert(sysResourceDatasource);
        sysResourceDatasource.setPassword(notEncryptPassword);
        return sysResourceDatasource;
    }
	
	@Override
    public void batchInsert(List<SysResourceDatasource> sysResourceDatasources){
		if (sysResourceDatasources != null && sysResourceDatasources.size() > 0) {
	        sysResourceDatasources.forEach(sysResourceDatasource -> {
				sysResourceDatasource.setDatasourceId(UUID.randomUUID().toString());
				if(!StringUtils.isEmpty(sysResourceDatasource.getPassword())) {
                    sysResourceDatasource.setPassword(encrypt(sysResourceDatasource.getPassword(), secretKey));
                }
            });
            sysResourceDatasourceMapper.batchInsert(sysResourceDatasources);
        }
        
    }

    @Transactional(rollbackFor = Exception.class)
	@Override
    public void deleteSysResourceDatasource(String datasourceId){
        SysResourceDatasource sysResourceDatasource = getById(datasourceId);
        sysResourceDatasourceMapper.delete(datasourceId);
        try {
            if ("udb".equalsIgnoreCase(sysResourceDatasource.getTypeCode()) || "udbx".equalsIgnoreCase(sysResourceDatasource.getTypeCode())) {
                storageClient.delete(sysResourceDatasource.getAddr(), SecurityUtilsExt.getUser().getAuthUser().getTenantId());
                if ("udb".equalsIgnoreCase(sysResourceDatasource.getTypeCode())) {
                    String addr = sysResourceDatasource.getAddr();
                    if (addr!= null && addr.endsWith(".udb")) {
                        String uddAddr = addr.replace(".udb", "udd");
                        storageClient.delete(uddAddr, SecurityUtilsExt.getUser().getAuthUser().getTenantId());
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Transactional(rollbackFor = Exception.class)
	@Override
    public void batchDelete(List<String> datasourceIds){
        if (datasourceIds == null || datasourceIds.isEmpty()) {
            return;
        }
        for (String datasourceId : datasourceIds) {
            deleteSysResourceDatasource(datasourceId);
        }
    }
	
	@Override
    public SysResourceDatasource updateSysResourceDatasource(SysResourceDatasource sysResourceDatasource){
        if(!StringUtils.isEmpty(sysResourceDatasource.getPassword())) {
            sysResourceDatasource.setPassword(encrypt(sysResourceDatasource.getPassword(), secretKey) );
        }
        sysResourceDatasourceMapper.update(sysResourceDatasource);
        return sysResourceDatasource;
    }

    @Override
    public List<SysResourceDatasource> getDatasources(List<String> typeCodes, Boolean isTemplate) {
        SysResourceDatasourceSelectVo selectVo = new SysResourceDatasourceSelectVo();
        selectVo.setTypeCodes(typeCodes);
        selectVo.setTemplate(isTemplate);
        List<SysResourceDatasource> datasources = sysResourceDatasourceMapper.selectList(selectVo);
        datasources.forEach(sysResourceDatasource -> {
            if(!StringUtils.isEmpty(sysResourceDatasource.getPassword())) {
                sysResourceDatasource.setPassword(decrypt(sysResourceDatasource.getPassword(), secretKey));
            }
        });
        return datasources;
    }

    @Override
    public List<DefaultTreeNode> getTree() {
        List<DefaultTreeNode> allNodes = new LinkedList<>();
        DefaultTreeNode node1 = new DefaultTreeNode();
        node1.setKey("file");
        node1.setParentId("0");
        node1.setTitle("文件型");
        node1.setSortSn(1);
        allNodes.add(node1);

        DefaultTreeNode node2 = new DefaultTreeNode();
        node2.setKey("database");
        node2.setParentId("0");
        node2.setTitle("数据库型");
        node2.setSortSn(2);
        allNodes.add(node2);

        SysResourceDatasourceSelectVo selectVo = new SysResourceDatasourceSelectVo();
        selectVo.setSdx(true);
        selectVo.setTemplate(false);
        List<SysResourceDatasource> datasources = sysResourceDatasourceMapper.selectList(selectVo);
        List<SysResourceDatasource> collect = datasources.stream().peek(sysResourceDatasource -> sysResourceDatasource.setPassword(decrypt(sysResourceDatasource.getPassword(), secretKey))).collect(Collectors.toList());
        for (int i = 0; i < collect.size(); i++) {
            SysResourceDatasource datasource = collect.get(i);
            DefaultTreeNode node = new DefaultTreeNode();
            node.setKey(datasource.getDatasourceId());
            String typeCode = datasource.getTypeCode();
            if ("UDBX".equals(typeCode) || "UDB".equals(typeCode)) {
                node.setParentId("file");
            } else {
                node.setParentId("database");
            }
            node.setSortSn(i+1);
            node.setTitle(datasource.getDsName());
            DatasourceConnectionInfo datasourceConnectionInfo = new DatasourceConnectionInfo();
            BeanUtils.copyProperties(datasource, datasourceConnectionInfo);
            node.setUserObject(datasourceConnectionInfo);
            allNodes.add(node);
        }

        DefaultTreeNode root = new DefaultTreeNode();
        root.setKey("0");
        return TreeUtil.getChildren(root, allNodes, Comparator.comparingInt(DefaultTreeNode::getSortSn));
    }

    @Override
    public List<SysResourceDatasource> getByName(String dsName, Boolean isSdx,Boolean isTemplate) {
        return sysResourceDatasourceMapper.getByName(dsName,isSdx,isTemplate);
    }

}
